<?php
/**
 * Created by PhpStorm.
 * User: Inhere
 * Date: 15-4-1
 * Time: 上午10:08
 * Used: Parser
 * file: Parser.php
 */

namespace ulue\cli;

// fcgi doesn't have STDIN and STDOUT defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));


/**
 *
 */
class Parser implements \ArrayAccess, \IteratorAggregate
{
    public $basePath;

    public $script;

    protected $options = [];

    /**
     * $_SERVER['argv'] 的数据拷贝(除了第一个脚本信息项)
     * @var array
     */
    protected $rawArgs = [];

    protected $args = [];


    public function __construct($basePath=null)
    {
        if ( php_sapi_name() !== 'cli' ) {
            exit(':( Only allowed in the command line!!');
        }

        $this->basePath = $basePath;
        $this->parseArguments();
    }

    public function __get($name)
    {
        $getter = 'get' . ucfirst($name);

        if ( method_exists($this, $getter) ) {

            return $this->$getter($name);
        }

        return $this->get($name);
    }

    public function __set($name, $value)
    {
        $setter = 'set' . ucfirst($name);

        if (method_exists($this, $setter)) {
            $this->$setter($value);
        } else {
            $this->set($name, $value);
        }
    }

//////////////////////////////////////// attr rawArgs ////////////////////////////////////////

    /**
     * @return array
     */
    public function getRawArgs()
    {
        return $this->rawArgs;
    }

    /**
     * @param array $args
     * @return $this
     */
    public function setRawArgs(array $args)
    {
        $this->rawArgs = $args;

        return $this;
    }

    /**
     * @param $key
     * @param null $default
     * @return null
     */
    public function getRawArg($key, $default = null)
    {
        return isset($this->rawArgs[$key]) ? $this->rawArgs[$key] : $default;
    }

    /**
     * @param $key
     * @param $value
     * @return $this
     */
    public function setRawArg($key, $value)
    {
        $this->rawArgs[$key] = $value;

        return $this;
    }

//////////////////////////////////////// attr args ////////////////////////////////////////

    public function exist($name)
    {
        return isset($this->args[trim($name)]);
    }

    public function existValue($name)
    {
        return in_array(trim($name), $this->args);
    }

    public function get($name, $default=null)
    {
        return $this->exist($name) ? $this->args[trim($name)] : $default;
    }

    public function def($name, $value)
    {
        $this->args[trim($name)] = $value;

        return $this;
    }

    public function set($name, $value)
    {
        if ($this->exist($name)) {
            $this->args[trim($name)] = $value;
        }

        return $this;
    }

    public function getArgs()
    {
        return $this->args;
    }

    public function setArgs(array $args)
    {
        $this->args = $args;

        return $this;
    }

//////////////////////////////////////// Interactive ////////////////////////////////////////

    /**
     * 确认, 发出信息要求确认；返回 true | false
     * @param  string $question 发出的信息
     * @return bool
     */
    public function confirm($question)
    {
        $question = ucfirst(trim($question));

        echo "\n    $question  \n    Please confirm [y|n] : ";

        $answer = $this->readLine();

        return !strncasecmp($answer, 'y', 1);
    }

    /**
     * 询问，提出问题；返回 输入的结果
     * @param  string $question 问题
     * @return string
     */
    public function ask($question)
    {
        if ($question) {
            $question = ucfirst(trim($question));

            echo "\n    $question ";
        }

        return $this->readLine();
    }

    /**
     * 持续询问，提出问题；
     * 若输入了值则返回 输入的结果
     * 否则，会连续询问三次， 若任然没有输入任何值，退出
     * @param  string $question 问题
     * @return string
     */
    public function loopAsk($question)
    {
        $question = ucfirst(trim($question));
        $loop  = true;
        $key  = 1;

        while ($loop) {
            echo "\n    $question ";
            $answer = $this->readLine();

            if ($answer !== '') {
                break;
            }

            if ($key == 3) {
                exit("\n You have input error three times in succession!!, ByeBye.\n");
            }

            $key++;
        }

        /** @var string $answer */
        return $answer;
    }

    /**
     * 读取输入信息
     * @return string
     */
    public function readLine()
    {
        return trim(fgets(STDIN));
    }

    /**
     * 原样输出，不添加换行符
     * @param  string  $value
     * @param  boolean $exit
     */
    public function rawOut($value, $exit=false)
    {
        echo $value;

        $exit && exit();
    }

    /**
     * 输出，会在前后添加换行符
     * @param  string  $value
     * @param  boolean $exit
     */
    public function out($value, $exit=false)
    {
        echo "\n$value";

        $exit && exit();
    }


    /**
     * 在命令行执行命令时候，获取传入的参数
     * 可在设置 多个参数
     * @example /var/www/project$ ulue aaa --env=dev ccc --name=tony --aa=bbb
     * 返回为
     * [
     *  0       => 'aaa',
     *  1       => 'ccc',
     *  'env'   => 'dev',
     *  'name'  => 'tony',
     *  'aa'    => 'bbb',
     * ]
     */
    protected function parseArguments()
    {
        $rawParams = [];

        if (isset($_SERVER['argv'])) {
            $rawParams = $_SERVER['argv'];
            $this->script = array_shift($rawParams);
            $this->rawArgs = $rawParams;
        }

        $params = [];

        foreach ($rawParams as $param) {
            if (preg_match('/^--(\w+)(=(.*))?$/', $param, $matches)) {
                $name          = $matches[1];
                $this->args[$name] = $params[$name] = isset($matches[3]) ? $matches[3] : true;
            } elseif (preg_match('/^(\w+)(=(.*))?$/', $param, $matches)) {
                $name          = $matches[1];
                $this->options[$name] = isset($matches[3]) ? $matches[3] : true;
            } else {
                $params[] = $param;
            }
        }

        // return $params;
    }

///////////////////////////////// Array Iterator /////////////////////////////////

    /**
     * Defined by IteratorAggregate interface
     * Returns an iterator for this object, for use with foreach
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->args);
    }

    /**
     * Checks whether an offset exist in the iterator.
     * @param   mixed  $offset  The array offset.
     * @return  boolean  True if the offset exist, false otherwise.
     */
    public function offsetExists($offset)
    {
        return (boolean) ($this->get($offset) !== null);
    }

    /**
     * Gets an offset in the iterator.
     * @param   mixed  $offset  The array offset.
     * @return  mixed  The array value if it exist, null otherwise.
     */
    public function offsetGet($offset)
    {
        return $this->get($offset);
    }

    /**
     * Sets an offset in the iterator.
     * @param   mixed  $offset  The array offset.
     * @param   mixed  $value   The array value.
     * @return  void
     */
    public function offsetSet($offset, $value)
    {
        $this->set($offset, $value, true);
    }

    /**
     * Unset an offset in the iterator.
     * @param   mixed  $offset  The array offset.
     * @return  void
     */
    public function offsetUnset($offset)
    {
        $this->set($offset, null);
    }

///////////////////////////////// other attr /////////////////////////////////


} // end class